package hr.fer.apr.ht41337.lab2;

import java.util.Random;

import hr.fer.apr.ht41337.lab2.funkcije.*;


public class MinimizacijaPoBoxu {
	
	private static int n;
	
	public static Tocka minimum(FunkcijaOgranicena f, Tocka x0, double e, double a)
	{
		
		if (! ( f.zadovoljavaEksplicitnaOgranicenja(x0) && f.zadovoljavaImplicitnaOgranicenja(x0) ) )
			throw new IllegalArgumentException("Tocka ne zadovoljava ogranicenja");
		
		n = f.getDimenzija();
		
		Tocka[] x = generirajTocke(f, x0);
		Tocka xc = new Tocka(n);
		boolean zaustavi = false;
		double simplex;
		int br = 0;
		while (!zaustavi)
		{
			br++;
			
			int h = 0;
			for (int i = 1; i < 2*n; i++)
				if (f.getValue(x[i]) > f.getValue(x[h]))
					h = i;
			int h2 = (h == 0 ? 1 : 0);
			for (int i = h2 + 1; i < 2*n; i++)
				if (f.getValue(x[i]) > f.getValue(x[h2]) && i != h)
					h2 = i;
			
			
			xc = new Tocka(n);
			for (int i = 0; i < 2 * n; i++)
				if ( i != h)
					xc = xc.plus(x[i]);
			xc = xc.times( 1.0/(2 * n - 1) );
			
			Tocka xr = xc.times(1+a).plus(x[h].times(-a));  // xr = (1+a)*xc - a*x[h]
			
			
			for (int i = 0; i < n; i++)
			{
				if (xr.getX(i) < f.getXd(i))
					xr.setX(i, f.getXd(i));
				else if (xr.getX(i) > f.getXg(i))
					xr.setX(i, f.getXg(i));
			}

			
			while ( !f.zadovoljavaImplicitnaOgranicenja(xr))
				xr = (xr.plus(xc)).times(0.5); 	// xr = 1/2 * (xr + xc)
			
			
			if (f.getValue(xr) > f.getValue(x[h2]))
				xr = (xr.plus(xc)).times(0.5); 	// xr = 1/2 * (xr + xc)

			x[h] = xr.clone();
			
			//System.out.println(xc);
			simplex = 0;
			for (int i = 0; i < n; i++)
				simplex += Math.pow(f.getValue(x[i]) - f.getValue(xc),2);
			simplex /= n;
			simplex = Math.pow(simplex, 0.5);

			if (simplex < e)
				zaustavi = true;
			
		}
		//System.out.println(br);
		return xc;
	}

	private static Tocka[] generirajTocke(FunkcijaOgranicena f, Tocka xc)
	{
		Tocka[] x = new Tocka[ 2 * n ];
		Random generator = new Random();
		double r;
		double value;
		
		for (int t = 0; t < 2 * n ; t++)
		{
			//generiraj novu tocku unutar eksplicitnih ogranicenja
			x[t] = new Tocka(n);
			for (int i = 0; i < n; i++)
			{
				r = generator.nextDouble();
				value = f.getXd(i) + r * (f.getXg(i) - f.getXd(i));
				x[t].setX(i, value);	
			}
			
			//dok nisu zadovoljena implicitna ogranicenja, pomakni tocku prema centroidu
			while ( !f.zadovoljavaImplicitnaOgranicenja(x[t]))
				x[t] = (x[t].plus(xc)).times(0.5); 	// x[t] = 1/2 * (x[t] + xc)
	
			//izracunaj novi xc
			xc = x[0].clone();
			for (int i = 1; i <= t; i++)
				xc = xc.plus(x[i]);
			xc = xc.times(1.0/(t+1));
			
		}
		return x;
	}
}
